package eu.briano.suirvellancecamera;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.android.AndroidAuthSession;
import com.dropbox.client2.session.AccessTokenPair;
import com.dropbox.client2.session.AppKeyPair;
import com.dropbox.client2.session.Session.AccessType;

import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Environment;


public class Utils {

	public final static  long minSendElapse = 2500;
	public final static int REQUEST_LINK_TO_DBX = 12120;
	public final static  AccessType ACCESS_TYPE = AccessType.APP_FOLDER;
	public final static String logName = "SuirvellanceCamera";
	public final static String agentName ="yasc-agent";
	

	
	public final static String dropBoxTokenKey	 = "dropbox-userToken";
	public final static String dropBoxTokenSecret  = "dropbox-userSecret";
	public final static String yascPreferencesName ="yasc-dropox";

	public final static int imageQuality = 90;
	public final  static double pixelNoisePct = 0.05;
	public final static int ipSize =64; // 32px
	public final static int ipSizeArea = ipSize*ipSize;
	public final static double pixelNoise = 255*pixelNoisePct;

	public final static float maxChange = 10/(1f*ipSizeArea);    // =0.01% of binary image changed	
	
	public final static double[] yuvScale = new double []{255,255,255};
	public static int yuvChannel = 0;
	public static int previewFormat = ImageFormat.NV21;
	
	
	public static long backgroundPollingPeriod = 30*1000;

	

	public final static double bgCalcIteration = 20;
	public final static float bgSnoozeCalcIteration = 360;
	public final static float noiseThreshold = 20/bgSnoozeCalcIteration;
	
	public static final long maxSendingFile = 2*2;





	public static long now() {
		return new Date().getTime();
	}

	static Bitmap readFlatBitmap(String bckFileName,int w , int h,Config config)  {
		try {
			Bitmap bm = Bitmap.createBitmap(w, h, config);
			ByteBuffer src = ByteBuffer.allocate(w*h*3);
			FileInputStream fIn;
			fIn = new FileInputStream(bckFileName);
			FileChannel fChan =  fIn.getChannel();
			fChan.read(src);
			src.rewind();
			bm.copyPixelsFromBuffer(src);
			return bm;
		} catch (Exception e) {
			Log.e(logName,"readFlatBitmap error ",e);
		}
		return null;

	}

	static void saveJpegImage(final byte[] buffer) {
		long startS = now() ;
		try {
			if (buffer != null){

				String fileName = Environment.getExternalStorageDirectory()+"/saved.jpg";
				FileOutputStream file = new FileOutputStream(fileName,false);
				file.write(buffer);
				file.close();
				Log.v(logName,"saved in  "+ ( now() - startS ) );

			}
		} catch (Throwable e) {
			Log.e(logName,"takePhoto",e);
		}
		Log.v(logName,"return  "+ ( now() - startS ) );
	}

	static void writeFlatBitmap(String bckFileName, Bitmap background) throws Exception {
		int h = background.getHeight();
		int w = background.getWidth();
		ByteBuffer dst = ByteBuffer.allocate(w*h*3);
		background.copyPixelsToBuffer(dst);
		dst.rewind();
		FileOutputStream fOut = new FileOutputStream(bckFileName);
		FileChannel fChan =  fOut.getChannel();
		fChan.write(dst);
	}

	static void logCameraInfos(Camera c) {
		if (c== null)
			return;
		try {
			Parameters params = c.getParameters();
			List<Size> previ = params.getSupportedPreviewSizes();
			for (Size s : previ) {
				Log.i(logName, "prev size "+ s.width + "x"+ s.height);
			}
		} catch (Exception e) {
		}


	}

	public static void saveImage(YuvImage image,String fileName) {
		if (image == null)
			return;
		long startS= now();
		try {
			FileOutputStream file = new FileOutputStream(fileName,false);
			image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), Utils.imageQuality ,file);
		} catch (FileNotFoundException e) {
			Log.e(logName,"saveImage error",e);
		}
		Log.v(logName,"saved in  "+ ( now() - startS ) );

	}
	public static String getAlarmPhotoPath(Calendar now) {

		String path = String.format("%s/%4d/%02d/%02d/", 
				Utils.getAppBasePath(),now.get(Calendar.YEAR),now.get(Calendar.MONTH)+1,now.get(Calendar.DAY_OF_MONTH));
		return path;
	}

	public static String getAlarmPhotoFileName(Calendar now) {

		String path = Utils.getAlarmPhotoPath(now)+String.format("%02d_%02d_%02d.jpg" ,
				now.get(Calendar.HOUR_OF_DAY),now.get(Calendar.MINUTE),now.get(Calendar.SECOND));
		return path;
	}

	public static String getAppBasePath() {
		return Environment.getExternalStorageDirectory()+"/yasc/";
	}
	public static String getWorkBasePath() {
		return getAppBasePath()+"work/";
	}
	public static String getDropBoxFileName(Calendar now,String postfix) {
		return getDropBoxFileName(now,postfix,"");
	}
	
	public static String getDropBoxFileName(Calendar now,String postfix,String prefix) {
		
		String base = String.format("/%4d/%02d/%02d/",now.get(Calendar.YEAR),now.get(Calendar.MONTH)+1,now.get(Calendar.DAY_OF_MONTH));
		base = base+String.format("%02d/"+prefix+"%02d_%02d.%04d"+postfix+".jpg" ,	 
				now.get(Calendar.HOUR_OF_DAY),now.get(Calendar.MINUTE),now.get(Calendar.SECOND),now.get(Calendar.MILLISECOND));
		return base;
	}

	public static boolean createDirs(String path) {
		File f = new File(path);
		if (f.exists() && f.isDirectory())
			return true;
		if (f.exists() && !f.isDirectory())
			return false;
		return f.mkdirs();
	}

	//private static long imageProg = 0;
	
	public static String formattedTimestamp(long nowL) {
		return nowL+"-0";
	}

	public static long parseFormattedTimestamp(String fileName) {
		String [] pieces = fileName.split("-");
		Log.v(Utils.logName,"parseFormattedTimestamp parsing "+ fileName + " found "+pieces.length+" pieces");		
		try {
			if (pieces.length > 0 )
				return Long.parseLong(pieces[0]);
		} catch (NumberFormatException e) {
			Log.d(Utils.logName,"parseFormattedTimestamp parse error  on : "+ fileName);
		}
		return 0;
	}

	public static String getSendFileBasePath() {
		
		return  getAppBasePath()+"sending/";
	}
	
	
	public static  AppKeyPair getDropBoxAppSecret(ContextWrapper ctx) throws IOException {
		Resources resources = ctx.getResources();
		InputStream is = resources.openRawResource(R.raw.keys);
		Properties p = new Properties();
		p.load(is);
		AppKeyPair app = new AppKeyPair(p.getProperty("APP_KEY"),
				p.getProperty("APP_SECRET"));
		return app;
	}
	
	public static DropboxAPI<AndroidAuthSession> dropBoxConnect(ContextWrapper ctx) {
		AppKeyPair appKeys;
		DropboxAPI<AndroidAuthSession> mDBApi = null;
		try {
			appKeys = Utils.getDropBoxAppSecret(ctx);
			AndroidAuthSession session = new AndroidAuthSession(appKeys,
					Utils.ACCESS_TYPE);
			mDBApi = new DropboxAPI<AndroidAuthSession>(session);
			AccessTokenPair access = getStoredKeys(ctx);
			if (access != null) {
				mDBApi.getSession().setAccessTokenPair(access);
			} else {
				Log.v(Utils.logName, "DropBox cannot connect");
			}
		} catch (IOException e) {
			Log.e(Utils.logName, "DropBox dropBoxConnect error", e);
		}
		return mDBApi;

	}	
	
	private static AccessTokenPair getStoredKeys(ContextWrapper ctx) {
		SharedPreferences sp = ctx.getSharedPreferences(Utils.yascPreferencesName,	ContextWrapper.MODE_PRIVATE);
		String k = sp.getString(Utils.dropBoxTokenKey, null);
		String s = sp.getString(Utils.dropBoxTokenSecret, null);
		Log.v(Utils.logName, "DropBox k:" + k + " s:" + s);
		if (k == null || s == null)
			return null;
		return new AccessTokenPair(k, s);
	}

	public static int[] selectCameraPreviewSize(Camera c) {
		int size [] = new int [] {800,480};
		if (c== null)
			return size ;
		try {
			Parameters params = c.getParameters();
			List<Size> previ = params.getSupportedPreviewSizes();
			for (Size s : previ) {
				if (s.width %32 != 0 ||  s.height % 32 != 0) 
					continue;
				if (s.width < s.height)
					continue;
				if (s.width > size[0]) {
					size[0] = s.width;
					size[1] = s.height;
				}
			}
		} catch (Throwable e) {
			Log.v(logName, "selectCameraPreviewSize error",e);
		}
		Log.i(logName, "selectCameraPreviewSize selected"+size[0]+"x"+size[1]);
		return size;
	}
	public  static String readFile(String path) throws IOException {
		  FileInputStream stream = new FileInputStream(new File(path));
		  try {
		    FileChannel fc = stream.getChannel();
		    MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
		    /* Instead of using default, pass in a decoder. */
		    return Charset.defaultCharset().decode(bb).toString();
		  }
		  finally {
		    stream.close();
		  }
		}	
	
}
